﻿/* **************************************************************************
 *
 * Copyright 2007-2010 Jeff Hardy
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * *************************************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using IronPython.Hosting;
using IronPython.Runtime;
using Microsoft.Scripting.Hosting;

namespace NWSGI
{
    public delegate IEnumerable WsgiCallableDelegate(PythonDictionary environ, StartResponseDelegate start_response);
    public delegate void WriteDelegate(string body_data);
    public delegate WriteDelegate StartResponseDelegate(string status, List response_headers, [DefaultParameterValue(null)]PythonTuple exc_info);

    public class Wsgi
    {
        private IWsgiConfig config;
        private ScriptEngine engine;
        private WsgiApplicationCache applications;
        private ICollection<string> searchPaths;

        public Wsgi(IWsgiConfig config)
        {
            this.config = config;
            this.engine = InitializeEngine();

            this.applications = new WsgiApplicationCache(engine, config);
        }

        public void Run(IWsgiContext context)
        {
            engine.SetSearchPaths(searchPaths = searchPaths ?? GetSearchPaths(context));

            WsgiApplication application = applications.GetApplication(context);
            if(application == null)
                throw new InvalidOperationException("There is no suitable application to service this request.");

            application.Run(context);
        }

        public void LoadAssembly(System.Reflection.Assembly assembly)
        {
            engine.Runtime.LoadAssembly(assembly);
        }

        /// <summary>
        /// Create an IronPython engine instance.
        /// </summary>
        /// <returns>An initialized PythonEngine.</returns>
        private ScriptEngine InitializeEngine()
        {
            Dictionary<string, object> options = new Dictionary<string, object>();
            options["Debug"] = config.DebugMode;
            options["AdaptiveCompilation"] = config.Compilation;
            options["Tracing"] = config.Tracing;
            options["LightweightScopes"] = config.LightweightScopes;
            options["EnableProfiler"] = config.EnableProfiler;

            switch(config.Compilation)
            {
                case CompilationOptions.Adaptive:
                    options["AdaptiveCompilation"] = true;
                    break;

                case CompilationOptions.Compiled:
                    options["AdaptiveCompilation"] = false;
                    break;
            }

            switch(config.Frames)
            {
                case FrameOptions.Off:
                    options["Frames"] = options["FullFrames"] = false;
                    break;

                case FrameOptions.On:
                    options["Frames"] = true;
                    break;

                case FrameOptions.Full:
                    options["Frames"] = options["FullFrames"] = true;
                    break;
            }

            ScriptEngine engine = Python.CreateEngine(options);

            // load the same assemblies as the console for consistency
            engine.Runtime.LoadAssembly(typeof(string).Assembly);

            if(config.DebugMode)
                engine.Runtime.LoadAssembly(typeof(System.Diagnostics.Debug).Assembly);

            return engine;
        }

        /// <summary>
        /// Get the search paths for the current request. This includes the paths
        /// from configuration as well as any from the Wsgi context.
        /// </summary>
        /// <param name="context">Current Wsgi context.</param>
        /// <returns>List of all paths to search.</returns>
        private ICollection<string> GetSearchPaths(IWsgiContext context)
        {
            List<string> paths = new List<string>(context.SearchPaths);

            foreach(string path in config.PythonPaths)
            {
                if(!string.IsNullOrEmpty(path))
                    paths.Add(context.MapPath(path));
            }

            return paths.AsReadOnly();
        }
        
        internal static System.Text.Encoding Latin1 = System.Text.Encoding.GetEncoding("iso-8859-1");
    }
}
